package com.youlu.campus.service.ca.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.common.utils.ExcelUtil;

import com.youlu.campus.common.utils.TimeUtils;
import com.youlu.campus.entity.*;
import com.youlu.campus.entity.VO.req.*;
import com.youlu.campus.entity.ca.GiftCaInfo;
import com.youlu.campus.entity.domain.CaImport;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.base.BaseService;
import com.youlu.campus.service.ca.CaGroupInfoService;
import com.youlu.campus.service.ca.CaInfoMapppingService;
import com.youlu.campus.service.ca.CaInfoService;
import com.youlu.campus.service.ca.CaMaterialConfigService;
import com.youlu.campus.service.cache.CacheCaInfoService;
import com.youlu.campus.service.config.WxPayConfig;
import com.youlu.campus.service.item.ItemStockService;
import com.youlu.campus.service.utils.PayUtil;
import com.youlu.campus.service.visitor.VisitorUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
@Service
public class CaInfoServiceImpl extends BaseService implements CaInfoService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private WxPayConfig wxPayConfig;
    @Autowired
    private VisitorUserService visitorUserService;
    @Autowired
    private CaGroupInfoService caGroupInfoService;

    @Autowired
    @Lazy
    private ActivityService activityService;
    @Value("${sys.qq.appId}")
    protected String appId;
    @Autowired
    private CacheCaInfoService cacheCaInfoService;
    @Autowired
    private CaMaterialConfigService caMaterialConfigService;
    @Autowired
    private ItemStockService itemStockService;
    @Autowired
    private CaInfoMapppingService caInfoMapppingService;

    @Override
    public Page<CaInfo> list(CaInfoQueryVO req) {
        log.info(":>>> 开始获取证书列表:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getCaGroupInfoId())) {
            log.error(":>>> 获取证书列表参数错误:证书分组为空");
            throw new BusinessException("获取证书列表参数错误");
        }
        Query query = new Query();
        if (StringUtils.isNotBlank(req.getCaGroupInfoId())) {
            query.addCriteria(Criteria.where("caGroupInfoId").is(req.getCaGroupInfoId()));
        }
        if (StringUtils.isNotBlank(req.getActivityId())) {
            query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
        }
        if (Objects.nonNull(req.getCombinedCommodity())) {
            query.addCriteria(Criteria.where("combinedCommodity").is(req.getCombinedCommodity()));
        }
        if (StringUtils.isNotBlank(req.getName())) {
            if (StringUtils.isNotBlank(req.getName())) {
                Pattern pattern = Pattern.compile("^.*" + req.getName() + ".*$", Pattern.CASE_INSENSITIVE);
                query.addCriteria(Criteria.where("name").regex(pattern));
            }
        }
        query.addCriteria(Criteria.where("deleted").ne(true));
        long count = mongoTemplate.count(query, CaInfo.class);
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort = Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<CaInfo> infos = mongoTemplate.find(query.with(pageRequest), CaInfo.class);
        for (CaInfo caInfo : infos) {
            List<CaMaterialConfig> caMaterialIdList =
                    caMaterialConfigService.findByCaMaterialIdList(caInfo.getItemMaterialList());
            if (CollectionUtils.isNotEmpty(caMaterialIdList)) {
                caInfo.setMaterialConfigName(caMaterialIdList.stream().map(CaMaterialConfig::getName).collect(Collectors.joining(",")));
            }
            Integer s = itemStockService.getStock(caInfo.getActivityId(), caInfo.getId());
            caInfo.setOccupiedStock(s);
            if (ArrayUtils.isNotEmpty(caInfo.getCertificateImages())) {
                caInfo.setCertificateImage(caInfo.getCertificateImages()[0]);
            }
        }
        return PageableExecutionUtils.getPage(infos, pageRequest, () -> count);
    }

    @Override
    public CaInfo findOne(String id) {
//        log.info(":>>> 开始获取证书:{}", id);
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 获取证书参数错误");
            throw new BusinessException("获取证书详情参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        CaInfo caInfo = mongoTemplate.findOne(query, CaInfo.class);
        if (Objects.isNull(caInfo)) {
            return null;
        }
        if (StringUtils.isNotBlank(caInfo.getActivityId())) {
            Query queryA = new Query().addCriteria(Criteria.where("id").is(caInfo.getActivityId()));
            ActivityInfo a = mongoTemplate.findOne(queryA, ActivityInfo.class);
            if (Objects.nonNull(a)) {
                caInfo.setActivityName(a.getName());
            }
        }
        if (StringUtils.isNotBlank(caInfo.getGiftMaterialConfigId()) && CollectionUtils.isEmpty(caInfo.getGiftItems())) {
            CaInfo gift = this.findCaInfoById(caInfo.getGiftMaterialConfigId());
            if (Objects.nonNull(gift)) {
                GiftCaInfo giftCaInfo = new GiftCaInfo();
                BeanUtils.copyProperties(gift, giftCaInfo);
                List<GiftCaInfo> gifts = new ArrayList<>();
                gifts.add(giftCaInfo);
                caInfo.setGiftItems(gifts);
            }
        }
        if (Boolean.TRUE.equals(caInfo.getCombinedCommodity())) {
            List<CaInfo> childs = this.findByParentId(caInfo.getId());
//            caInfoMapppingService.findByParentId(caInfo.getId());
//            Query q = new Query();
//            q.addCriteria(Criteria.where("parentId").is(caInfo.getId()));
//            List<CaInfo> childs = mongoTemplate.find(q, CaInfo.class);
            caInfo.setChildrenItem(childs);
        }
//        log.info(":>>> 证书:{},信息为:{}", id, caInfo == null ? null : caInfo.getName() + ",材质:" + caInfo
//        .getMaterialConfigId());
        return caInfo;
    }

    @Override
    public Map<String, Object> caImport(byte[] inputStream, String fileName, String caGroupId) {
        log.info("orderImport fileName {}", fileName);
        if (StringUtils.isBlank(caGroupId)) {
            log.error(":>>> 导入证书错误:caGroupId为空");
            throw new BusinessException("caGroupId为空");
        }
        CaGroupInfo caGroupInfo = caGroupInfoService.findOne(caGroupId);
        if (Objects.isNull(caGroupInfo)) {
            log.error(":>>> 证书分组为空:{}", caGroupId);
            throw new BusinessException("证书分组为空");
        }
        Date date = new Date();
        try {
            Map<String, Object> result = new HashMap<>();
            List<CaImport> models = ExcelUtil.readData(new ByteArrayInputStream(inputStream), CaImport.class);
            result.put("allCount", models.size());
            int matchCount = 0;
            int notMatchCount = 0;
            List<Integer> notMatchRows = new ArrayList<>();
            for (int i = 0; i < models.size(); i++) {
                CaImport model = models.get(i);
                log.info(":>>> 导入行:{},数据:{}", i, JSON.toJSON(model));
                if (Objects.isNull(model)) {
                    log.error(":>>> 导入参数错误");
                    continue;
                }
                CaInfo caInfo = new CaInfo();
                caInfo.setId(null);
                caInfo.setCreatedTime(date);
                caInfo.setActivityId(caGroupInfo.getActivityId());
                caInfo.setActivityName(caGroupInfo.getActivityName());
                caInfo.setCaGroupInfoId(caGroupId);
                caInfo.setCaGroupInfoName(caGroupInfo.getName());
                caInfo.setPriority(model.getPriority());
                caInfo.setEdition(model.getEdition());
                caInfo.setName(model.getName());
                caInfo.setLevel(model.getLevel());
                caInfo.setPrice(model.getPrice());
                caInfo.setOriginalPrice(caInfo.getPrice());
                caInfo.setCostPrice(caInfo.getPrice());
                caInfo.setNum(model.getPrice());
                caInfo.setExpressFee(0);
                caInfo.setItemType(model.getItemType());
                if (model.getChecked() == 1) {
                    caInfo.setChecked(Boolean.TRUE);
                } else {
                    caInfo.setChecked(Boolean.FALSE);
                }
                caInfo.setInStockTips(model.getInStockTips());
                caInfo.setNoStockTips(model.getNoStockTips());
                caInfo.setGift(model.getGift());
                caInfo.setCaTag(model.getCaTag());
                caInfo.setCaRemark(model.getCaRemark());
                if (model.getCanOverbought() == 0) {
                    caInfo.setCanOverbought(Boolean.FALSE);
                } else {
                    caInfo.setCanOverbought(Boolean.TRUE);
                }
                // 增加特征
                List<String> caFeatures = Lists.newArrayListWithExpectedSize(7);
                IntStream.rangeClosed(1, 7).forEach(item -> {
                    Object ob = getFieldByName(model, "caFeature" + item);
                    String re = ob == null ? null : ob.toString();
                    if (StringUtils.isNotBlank(re)) {
                        caFeatures.add(re);
                    }
                });
                if (CollectionUtils.isNotEmpty(caFeatures)) {
                    caInfo.setCaFeatures(caFeatures);
                }
                mongoTemplate.insert(caInfo);
            }
            result.put("matchCount", matchCount);
            result.put("notMatchCount", notMatchCount);
            result.put("notMatchRows", notMatchRows);
            return result;
        } catch (Exception e) {
            log.error("orderImport error fileName {}", fileName, e);
            return null;
        }
    }

    public static Object getFieldByName(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return null;
        }
        for (Class clazz = obj.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(obj);
            } catch (Exception ex) {
                log.error("getFieldByName error field name:{}", fieldName, ex);
            }
        }
        return null;
    }

    @Override
    public Page<CaInfo> listByGroupId(CaInfoQueryVO req) {
        log.info(":>>> 开始获取证书列表:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getCaGroupInfoId())) {
            log.error(":>>> 获取证书列表参数错误:证书分组为空");
            throw new BusinessException("获取证书列表参数错误");
        }
        Query queryG = new Query().addCriteria(Criteria.where("id").is(req.getCaGroupInfoId()));
        CaGroupInfo groupInfo = mongoTemplate.findOne(queryG, CaGroupInfo.class);
        Query query = new Query();

        if (Objects.nonNull(groupInfo) && StringUtils.isNotBlank(groupInfo.getActivityId())) {
            query.addCriteria(Criteria.where("activityId").is(groupInfo.getActivityId()));
        }
        if (StringUtils.isNotBlank(req.getName())) {
            if (StringUtils.isNotBlank(req.getName())) {
                Pattern pattern = Pattern.compile("^.*" + req.getName() + ".*$", Pattern.CASE_INSENSITIVE);
                query.addCriteria(Criteria.where("name").regex(pattern));
            }
        }
        if (Objects.nonNull(req.getCombinedCommodity())) {
            query.addCriteria(Criteria.where("combinedCommodity").is(req.getCombinedCommodity()));
        }
        query.addCriteria(Criteria.where("deleted").ne(true));
        long count = mongoTemplate.count(query, CaInfo.class);
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort = Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<CaInfo> infos = mongoTemplate.find(query.with(pageRequest), CaInfo.class);
        for (CaInfo caInfo : infos) {
            Integer s = itemStockService.getStock(caInfo.getActivityId(), caInfo.getId());
            //兼容老数据
            if (s > 0) {
                caInfo.setOccupiedStock(s);
            } else {
                Integer soldCount = caMaterialConfigService.getBatchSoldCount(caInfo.getItemMaterialList());
                caInfo.setOccupiedStock(soldCount);
            }
            caInfo.setOccupiedStock(s);
        }
        return PageableExecutionUtils.getPage(infos, pageRequest, () -> count);
    }

    @Override
    public CaInfo findCaInfoById(String id) {
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 商品信息ID为空");
            throw new BusinessException("商品信息ID为空");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        CaInfo caInfo = mongoTemplate.findOne(query, CaInfo.class);
        return caInfo;
    }

    @Override
    public List<CaInfo> findByMaterialConfigId(String activityId, String materialConfigId) {
        log.info(":>>> 开始查找商品activityId:{},materialConfigId:{}", activityId, materialConfigId);
        Query query =
                new Query().addCriteria(Criteria.where("activityId").is(activityId).and("materialConfigId").is(materialConfigId));
        return mongoTemplate.find(query, CaInfo.class);
    }

    @Override
    public List<CaInfo> findByMaterialConfigId(String activityId, List<String> materialConfigIds) {
        log.info(":>>> 开始查找商品activityId:{},materialConfigId:{}", activityId, JSON.toJSON(materialConfigIds));
        Query query =
                new Query().addCriteria(Criteria.where("activityId").is(activityId).and("materialConfigId").in(materialConfigIds));
        return mongoTemplate.find(query, CaInfo.class);
    }

    @Override
    public List<CaInfo> find(@NotNull String activityId, @NotNull String level) {

        Query query = new Query().addCriteria(Criteria.where("activityId").is(activityId).and("level").is(level));
        return mongoTemplate.find(query, CaInfo.class);
    }

    @Async
    @Override
    public void copyCaInfo(CopyCaInfoReq copyCaInfoReq) {
        String copyActivityId = copyCaInfoReq.getCopyActivityId();
        String copySourceCaGroupId = copyCaInfoReq.getCopySourceCaGroupId();
        log.info("【copy 活动证书序列】开始复制活动证书序列->{} copyActivityId", copyActivityId);
        Query query = new Query();
        query.addCriteria(Criteria.where("caGroupInfoId").is(copySourceCaGroupId));
        query.addCriteria(Criteria.where("activityId").is(copyActivityId));
        List<CaInfo> caInfoList = this.mongoTemplate.find(query, CaInfo.class);
        if (CollectionUtils.isEmpty(caInfoList)) {
            log.error("【copy 活动】结束复制活动证书序列->{} 未查询到 copyActivityId", copyActivityId);
            return;
        }
        Date now = new Date();
        List<CaInfo> targetCaInfoList = caInfoList.stream().peek(caInfo -> {
            caInfo.setId(null);
            caInfo.setCreatedTime(now);
            caInfo.setUpdatedTime(now);
            caInfo.setCaGroupInfoId(copyCaInfoReq.getTargetCaGroupId());
            caInfo.setActivityId(copyCaInfoReq.getTargetCopyActivityId());
        }).collect(Collectors.toList());
        this.mongoTemplate.insertAll(targetCaInfoList);
        log.info("【copy 活动证书序列】结束复制活动证书序列->{} copyActivityId", copyActivityId);

    }

    @Override
    public List<CaInfo> findByActivityId(String activityId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("activityId").is(activityId));
        query.addCriteria(Criteria.where("deleted").is(Boolean.FALSE));
        return this.mongoTemplate.find(query, CaInfo.class);
    }

    @Override
    public List<CaInfo> findByParentId(String parentId) {
        List<CaInfoMappping> children = caInfoMapppingService.findByParentId(parentId);
        if (CollectionUtils.isEmpty(children)) {
            return new ArrayList<>();
        }
        List<String> cids = new ArrayList<>();
        for (CaInfoMappping caInfoMappping : children) {
            if (StringUtils.isNotBlank(caInfoMappping.getCaInfoId())) {
                if (!cids.contains(caInfoMappping.getCaInfoId())) {
                    cids.add(caInfoMappping.getCaInfoId());
                }
            }
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("id").in(cids));
        List<CaInfo> caInfoList = mongoTemplate.find(query, CaInfo.class);
        log.info(":>>> 商品:{},子商品个数为:{}", parentId, caInfoList.size());
        return caInfoList;
//        List<CaInfo> caInfoList = new ArrayList<>();
//        for (CaInfoMappping caInfoMappping : children) {
//            CaInfo caInfo = this.findCaInfoById(caInfoMappping.getCaInfoId());
//            if (Objects.nonNull(caInfo)) {
//                caInfoList.add(caInfo);
//            }
//        }
//        return caInfoList;
    }

    @Override
    public List<CaInfo> findWithQuery(Query q) {
        return this.mongoTemplate.find(q, CaInfo.class);
    }

    @Override
    public boolean create(CaInfo req) {
        log.info(":>>> 开始创建证书:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getCaGroupInfoId()) || StringUtils.isBlank(req.getEdition()) ||
                StringUtils.isBlank(req.getCertificateImage())
                || Objects.isNull(req.getPrice()) || StringUtils.isBlank(req.getLevel())
                || Objects.isNull(req.getPriority())) {
            log.error(":>>> 创建证书参数错误");
            throw new BusinessException("创建证书参数错误");
        }
        if (CollectionUtils.isEmpty(req.getItemMaterialList())) {
            log.error(":>>> 创建证书参数错误:证书材质配置错误");
            throw new BusinessException("证书材质配置错误");
        }
        if (StringUtils.isNotBlank(req.getGiftMaterialConfigId())) {
            CaInfo caInfo = this.findOne(req.getGiftMaterialConfigId());
            if (Objects.nonNull(caInfo)) {
                req.setGiftMaterialConfigName(caInfo.getName());
            }
        }
        if (Boolean.TRUE.equals(req.getCombinedCommodity())) {
            if (CollectionUtils.isEmpty(req.getChildrenItem())) {
                log.error(":>>> 组合装商品子商品为空");
                throw new BusinessException("组合装商品子商品为空");
            }
            Integer childrenTotalPrice = 0;
            for (CaInfo c : req.getChildrenItem()) {
                if (Objects.nonNull(c.getPrice())) {
                    childrenTotalPrice = c.getPrice() + childrenTotalPrice;
                }
            }
            if (childrenTotalPrice.compareTo(req.getPrice()) == 1) {
                log.error(":>>> 组合装商品子商品价格总和不能超过父商品，请检查！");
                throw new BusinessException(">>> 组合装商品子商品价格总和不能超过父商品，请检查！");
            }
        }
        CaMaterialConfig config = caMaterialConfigService.findOne(req.getMaterialConfigId());
        if (Objects.nonNull(config)) {
            req.setMaterialConfigName(config.getName());
        }
        req.setId(null);
        req.setCreatedTime(new Date());
        req.setCertificateNo(req.getGoodsCode());
        CaGroupInfo caGroupInfo = caGroupInfoService.findOne(req.getCaGroupInfoId());
        if (Objects.isNull(caGroupInfo)) {
            log.error(":>>> 证书分组:{} 不存在", req.getCaGroupInfoId());
            throw new BusinessException("证书分组不存在");
        }
        req.setCaGroupInfoName(caGroupInfo.getName());
        ActivityInfo activityInfo = activityService.findOne(caGroupInfo.getActivityId());
        if (Objects.nonNull(activityInfo)) {
            req.setActivityName(activityInfo.getName());
            req.setActivityId(activityInfo.getId());
        }
        if (Objects.isNull(req.getOnSale())) {
            req.setOnSale(Boolean.FALSE);
        }
        if (StringUtils.isBlank(req.getIconImageUrl())) {
            req.setIconImageUrl(req.getMainImageUrl());
        }
        req.setDeleted(false);
        req = mongoTemplate.insert(req);
        if (Boolean.TRUE.equals(req.getCombinedCommodity())) {
            if (CollectionUtils.isNotEmpty(req.getChildrenItem())) {
                boolean re = false;
                String tips = "";
                for (CaInfo c : req.getChildrenItem()) {
                    if (Boolean.TRUE.equals(c.getCombinedCommodity())) {
                        tips = tips + c.getName() + "||";
                        re = true;
                    }
                }
                if (re) {
                    throw new BusinessException("组合装子商品不能为组合装:" + tips);
                }
                for (CaInfo c : req.getChildrenItem()) {
                    CaInfoMappping caInfoMappping = new CaInfoMappping();
                    caInfoMappping.setParentId(req.getId());
                    caInfoMappping.setCaInfoId(c.getId());
                    caInfoMapppingService.create(caInfoMappping);
                }
            }
        }
        return true;
    }

    @Override
    public boolean update(CaInfo req) {
        log.info(":>>> 开始修改证书:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId())) {
            log.error(":>>> 修改证书参数错误");
            throw new BusinessException("修改证书参数错误");
        }
        CaInfo caInfo = mongoTemplate.findById(req.getId(), CaInfo.class);
        if (Objects.isNull(caInfo)) {
            log.error(":>>> 证书:{}不存在", req.getId());
            throw new BusinessException("证书不存在");
        }
        if (Boolean.TRUE.equals(req.getCombinedCommodity())) {
            Integer childrenTotalPrice = 0;
            for (CaInfo c : req.getChildrenItem()) {
                if (Objects.nonNull(c.getPrice())) {
                    childrenTotalPrice = c.getPrice() + childrenTotalPrice;
                }
            }
            log.info(":>>> 商品:{} 是组合装，子商品的价格总和:{},组合装商品价格:{}",req.getId(),
                    childrenTotalPrice,req.getPrice());
//            if (childrenTotalPrice < req.getPrice()) {
////            if (childrenTotalPrice.compareTo(req.getPrice()) == 1) {
//                log.error(":>>> 组合装商品子商品价格总和不能超过父商品，请检查！");
//                throw new BusinessException(">>> 组合装商品子商品价格总必须大于等于父商品，请检查！");
//            }
        }
        Date date = new Date();
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        Update update = new Update();
        update.set("updatedTime", new Date());

        if (StringUtils.isNotBlank(req.getName())) {
            update.set("name", req.getName());
        }
        update.set("detail", req.getDetail());

        if (StringUtils.isNotBlank(req.getCertificateImage())) {
            update.set("certificateImage", req.getCertificateImage());
        }
        update.set("canOverbought", req.getCanOverbought());
        update.set("certificateImages", req.getCertificateImages());
        if (Objects.nonNull(req.getPrice())) {
            update.set("price", req.getPrice());
        }
        if (Objects.nonNull(req.getOriginalPrice())) {
            update.set("originalPrice", req.getOriginalPrice());
        }
        if (Objects.nonNull(req.getCostPrice())) {
            update.set("costPrice", req.getCostPrice());
        }
        if (Objects.nonNull(req.getOnSale())) {
            update.set("onSale", req.getOnSale());
        }
        if (StringUtils.isNotBlank(req.getLevel())) {
            update.set("level", req.getLevel());
        }
        if (StringUtils.isNotBlank(req.getLevel())) {
            update.set("level", req.getLevel());
        }
        if (Objects.nonNull(req.getCaGift())) {
            update.set("caGift", req.getCaGift());
        }

        if (Objects.nonNull(req.getExpressFee())) {
            update.set("expressFee", req.getExpressFee());
        }
        update.set("gift", req.getGift());
        update.set("checked", req.getChecked());
        if (Objects.nonNull(req.getPriority())) {
            update.set("priority", req.getPriority());
        }
        if (StringUtils.isNotBlank(req.getEdition())) {
            update.set("edition", req.getEdition());
        }
        if (!CollectionUtils.isEmpty(req.getImageUrls())) {
            update.set("imageUrls", req.getImageUrls());
        }
        if (StringUtils.isNotBlank(req.getMaterialConfigId())) {
            update.set("materialConfigId", req.getMaterialConfigId());
            CaMaterialConfig config = caMaterialConfigService.findOne(req.getMaterialConfigId());
            if (Objects.nonNull(config)) {
                update.set("materialConfigName", config.getName());
            }
        }
        if (StringUtils.isNotBlank(req.getGiftMaterialConfigId())) {
            update.set("giftMaterialConfigId", req.getGiftMaterialConfigId());
            CaInfo caInfo1 = this.findOne(req.getGiftMaterialConfigId());
            if (Objects.nonNull(caInfo1)) {
                update.set("giftMaterialConfigName", caInfo1.getName());
            }
        }
        if (CollectionUtils.isNotEmpty(req.getGiftItems())) {
            update.set("giftItems", req.getGiftItems());
        } else {
            update.set("giftItems", new ArrayList<>());
        }
        update.set("expressFeeTips", req.getExpressFeeTips());
        if (Objects.nonNull(req.getInStockCycle())) {
            update.set("inStockCycle", req.getInStockCycle());
        }
        if (Objects.nonNull(req.getNoStockCycle())) {
            update.set("noStockCycle", req.getNoStockCycle());
        }
        if (StringUtils.isNotBlank(req.getInStockTips())) {
            update.set("inStockTips", req.getInStockTips());
        }
        if (StringUtils.isNotBlank(req.getNoStockTips())) {
            update.set("noStockTips", req.getNoStockTips());
        }
        if (Objects.nonNull(req.getNum())) {
            update.set("num", req.getNum());
        }
        if (Objects.nonNull(req.getCanOverbought())) {
            update.set("canOverbought", req.getCanOverbought());
        }
        if (Objects.nonNull(req.getCaTag())) {
            update.set("caTag", req.getCaTag());
        }
        if (Objects.nonNull(req.getCaFeatures())) {
            update.set("caFeatures", req.getCaFeatures());
        }
        if (Objects.nonNull(req.getCaRemark())) {
            update.set("caRemark", req.getCaRemark());
        }
        if (Objects.nonNull(req.getItemType())) {
            update.set("itemType", req.getItemType());
        }
        if (CollectionUtils.isNotEmpty(req.getItemMaterialList())) {
            update.set("itemMaterialList", req.getItemMaterialList());
        }
        if (req.getHasVirtualGoods() == null) {
            req.setHasVirtualGoods(false);
        }
        update.set("hasVirtualGoods", req.getHasVirtualGoods());
        if (StringUtils.isNotBlank(req.getVirtualGoodsList())) {
            update.set("virtualGoodsList", req.getVirtualGoodsList());
        }
        if (Objects.nonNull(req.getCombinedCommodity())) {
            update.set("combinedCommodity", req.getCombinedCommodity());
        }
        update.set("hasDiscount", req.getHasDiscount());
        update.set("discountFee", req.getDiscountFee());
        update.set("discountTips", req.getDiscountTips());
        update.set("hasActivityCoupon", req.getHasActivityCoupon());
        update.set("activityCouponNum", req.getActivityCouponNum());
        update.set("activityCouponFee", req.getActivityCouponFee());
        update.set("priceDesc", req.getPriceDesc());
        update.set("iconImageUrl", req.getIconImageUrl());
        update.set("doCaSubsidy", req.getDoCaSubsidy());
        update.set("autoAddCart", req.getAutoAddCart());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, CaInfo.class);
        cacheCaInfoService.remove(caInfo.getCaGroupInfoId(), caInfo.getLevel());
        cacheCaInfoService.remove(caInfo.getCaGroupInfoId(), req.getLevel());
        cacheCaInfoService.removeById(req.getId());
        boolean updateRe = updateResult.getModifiedCount() > 0 ? true : false;
        if (updateRe) {
            if (Boolean.TRUE.equals(req.getCombinedCommodity())) {
                List<CaInfoMappping> caInfoMapppings = caInfoMapppingService.findByParentId(req.getParentId());
                if (Objects.nonNull(caInfoMapppings) && caInfoMapppings.size() > 0) {
                    throw new BusinessException("商品父商品为组合装，不支持修改！");
                }
                if (CollectionUtils.isNotEmpty(req.getChildrenItem())) {
                    boolean re = false;
                    String tips = "";
                    for (CaInfo c : req.getChildrenItem()) {
                        if (Boolean.TRUE.equals(c.getCombinedCommodity())) {
                            tips = tips + c.getName() + "||";
                            re = true;
                        }
                    }
                    if (re) {
                        throw new BusinessException("组合装子商品不能为组合装:" + tips);
                    }
                    caInfoMapppingService.deleteByParentId(caInfo.getId());
                    for (CaInfo c : req.getChildrenItem()) {
                        CaInfoMappping caInfoMappping = new CaInfoMappping();
                        caInfoMappping.setParentId(caInfo.getId());
                        caInfoMappping.setCaInfoId(c.getId());
                        caInfoMapppingService.create(caInfoMappping);

                    }
                }
            }

        }
        return updateRe;
    }

    @Override
    public boolean delete(String tanentId, String id) {
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 删除证书参数错误");
            throw new BusinessException("删除证书参数错误");
        }
        CaInfo exist = mongoTemplate.findById(id, CaInfo.class);
        if (Objects.isNull(exist)) {
            log.error(":>>> 证书:{}不存在", id);
            throw new BusinessException("证书不存在");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
//        DeleteResult result = mongoTemplate.remove(query, CaInfo.class);
        Update update = new Update();
        update.set("deleted", true);
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, CaInfo.class);
        cacheCaInfoService.removeById(id);
        cacheCaInfoService.remove(exist.getCaGroupInfoId(), exist.getLevel());
        log.info(":>>> 删除证书:{},结果:{}", id, updateResult.getModifiedCount() > 0 ? true : false);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public Page<UserCaInfo> list(UserCaInfoQueryVO req) {
        log.info(":>>> 开始查询用户证书订单:{}", JSON.toJSONString(req));
        if (Objects.isNull(req)) {
            log.error(":>> 请求参数错误:{}", JSON.toJSONString(req));
            throw new BusinessException("参数错误");
        }
        Query query = new Query();
        if (StringUtils.isNotBlank(req.getName())) {
            query.addCriteria(Criteria.where("name").is(req.getName()));
        }
        if (StringUtils.isNotBlank(req.getMobile())) {
            Pattern pattern = Pattern.compile("^.*" + req.getMobile() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("mobile").regex(pattern));
        }
        if (Objects.nonNull(req.getMaterial())) {
            query.addCriteria(Criteria.where("material").is(req.getMaterial()));
        }
        if (StringUtils.isNotBlank(req.getUniversityId())) {
            query.addCriteria(Criteria.where("universityId").is(req.getUniversityId()));
        }
        if (StringUtils.isNotBlank(req.getActivityId())) {
            query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
        }
        if (StringUtils.isNotBlank(req.getOrderNo())) {

            Pattern pattern = Pattern.compile("^.*" + req.getOrderNo() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("orderNo").regex(pattern));
        }
        if (Objects.nonNull(req.getCaStatus())) {
            query.addCriteria(Criteria.where("caStatus").is(req.getCaStatus()));
        }
        if (Objects.nonNull(req.getVerifyStatus())) {
            query.addCriteria(Criteria.where("verifyStatus").is(req.getVerifyStatus()));
        }
        if (StringUtils.isNotBlank(req.getTag())) {
            Pattern pattern = Pattern.compile("^.*" + req.getTag() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("tagsSearchStr").regex(pattern));
        }
        if (StringUtils.isNotBlank(req.getLogisticsNo())) {
            Pattern pattern = Pattern.compile("^.*" + req.getLogisticsNo() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("logisticsNo").regex(pattern));
        }
        if (StringUtils.isNotBlank(req.getPlatform())) {
            query.addCriteria(Criteria.where("platform").is(req.getPlatform()));
        }
        long count = mongoTemplate.count(query, UserCaInfo.class);
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort = Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<UserCaInfo> infos = mongoTemplate.find(query.with(pageRequest), UserCaInfo.class);
        List<UserCaInfo> result = new ArrayList<>();
        for (UserCaInfo userCaInfo : infos) {
            if (StringUtils.isNotBlank(userCaInfo.getCaInfoId())) {
                Query query1 = new Query().addCriteria(Criteria.where("id").is(userCaInfo.getCaInfoId()));
                CaInfo caInfo = mongoTemplate.findOne(query1, CaInfo.class);
                if (Objects.nonNull(caInfo)) {
                    userCaInfo.setCaInfo(caInfo);
                    result.add(userCaInfo);
                }
            }
        }
        return PageableExecutionUtils.getPage(result, pageRequest, () -> count);
    }

    @Override
    public UserCaInfo getUserCaInfo(String tenantId, String id) {
        log.info(":>>> 开始获取证书订单:{}", id);
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        UserCaInfo userCaInfo = mongoTemplate.findOne(query, UserCaInfo.class);
        log.info(":>>> 证书订单:{}", JSON.toJSONString(userCaInfo));
        return userCaInfo;
    }

    @Override
    public List<String> createTags(CreateCaOrderTagVO req) {
        log.info(":>>> 添加证书订单标签:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId()) || StringUtils.isBlank(req.getTag())) {
            log.error(":>>> 添加证书标签参数错误");
            throw new BusinessException("参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        UserCaInfo userCaInfo = mongoTemplate.findOne(query, UserCaInfo.class);
        if (Objects.isNull(userCaInfo)) {
            log.error(":>>> 证书订单:{} 不存在", req.getId());
            throw new BusinessException("数据不存在");
        }
        if (userCaInfo.getTags().contains(req.getTag())) {
            return userCaInfo.getTags();
        }
        userCaInfo.getTags().add(req.getTag());
        if (StringUtils.isBlank(userCaInfo.getTagsSearchStr())) {
            userCaInfo.setTagsSearchStr(req.getTag());
        } else {
            userCaInfo.setTagsSearchStr(userCaInfo.getTagsSearchStr() + "," + req.getTag());
        }
        Update update = new Update();
        update.set("tags", userCaInfo.getTags());
        update.set("tagsSearchStr", userCaInfo.getTagsSearchStr());
        update.set("updatedTime", new Date());
        mongoTemplate.updateFirst(query, update, UserCaInfo.class);
        return userCaInfo.getTags();
    }

    @Override
    public boolean createComment(UserCaInfoComment req) {
        log.info(":>>> 开始创建订单备注:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getComment()) || StringUtils.isBlank(req.getUserCaInfoId())) {
            log.error(":>>> 创建备注错误,参数错误");
            throw new BusinessException("参数错误");
        }
        req.setCreatedTime(new Date());
        req.setStatus(Boolean.FALSE);
        req.setOptName(req.getOptName());
        mongoTemplate.insert(req);
        return true;
    }

    @Override
    public List<UserCaInfoComment> getComments(UserCaInfoComment req) {
        log.info("::>> 获取订单备注:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getUserCaInfoId())) {
            log.error(":>>> 获取备注错误,参数错误");
            throw new BusinessException("参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("userCaInfoId").is(req.getUserCaInfoId()));
        query.with(Sort.by(Sort.Order.asc("createdTime")));
        return mongoTemplate.find(query, UserCaInfoComment.class);
    }

    @Override
    public boolean shipping(UserCaInfo req) {
        log.info(":>>> 开始发货:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId()) || StringUtils.isBlank(req.getLogisticsNo()) || StringUtils.isBlank(req.getLogisticsCompany())) {
            log.error(":>>> 发货参数错误");
            throw new BusinessException("发货参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        UserCaInfo userCaInfo = mongoTemplate.findOne(query, UserCaInfo.class);
        if (Objects.isNull(userCaInfo)) {
            log.error(":>>> 订单不存在:{}", req.getId());
            throw new BusinessException("订单不存在");
        }
        /**
         * 状态,0:待付款,1:已付款,2:制作中,3:待发货,4:待收货,5:已收货,6:订单完成,7:订单关闭,8:售后中,9:退款中,10:待返货，11:返货中，12:退款完成
         */
        if (!userCaInfo.getCaStatus().equals(1) && !userCaInfo.getCaStatus().equals(2) && !userCaInfo.getCaStatus().equals(3)) {
            log.error(":>>> 订单状态:{},不是[已付款,制作中,待发货]", userCaInfo.getCaStatus());
            throw new BusinessException("订单状态错误");
        }
        Date date = new Date();
        Update update = new Update();
        update.set("caStatus", 4);
        update.set("logisticsNo", req.getLogisticsNo());
        update.set("logisticsCompany", req.getLogisticsCompany());
        update.set("shippingTime", date);
        update.set("updatedTime", date);
        UpdateResult result = mongoTemplate.updateFirst(query, update, UserCaInfo.class);
        boolean re = result.getModifiedCount() > 0 ? true : false;
        log.info(":>>> 订单:{} 发货状态为:{}", req.getId(), re);
        if (re) {

        }
        return re;
    }

    @Override
    public boolean preRefund(UserCaInfo req) {

        log.info(":>>> 开始退款预操作:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId()) || Objects.isNull(req.getRefundFee()) || StringUtils.isBlank(req.getRefundDesc())) {
            log.error(":>>> 退款操作参数错误");
            throw new BusinessException("退款操作参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        UserCaInfo userCaInfo = mongoTemplate.findOne(query, UserCaInfo.class);
        if (Objects.isNull(userCaInfo)) {
            log.error(":>>> 证书订单不存在:{}", userCaInfo.getId());
            throw new BusinessException("证书订单不存在");
        }
        UserPO userPO = visitorUserService.getUser(userCaInfo.getUserId());
        if (Objects.isNull(userPO)) {
            log.error(":>>> 用户:{} 不存在", userCaInfo.getUserId());
            throw new BusinessException("证书用户不存在");
        }
        //状态,0:待付款,1:已付款,2:制作中,3:待发货,4:待收货,5:已收货,6:订单完成,7:订单关闭,8:售后中,9:退款中,10:待返货，11:返货中，12:退款完成
        if (userCaInfo.getCaStatus().equals(9) || userCaInfo.getCaStatus().equals(12)) {
            log.error(":>>> 订单:{} 已经是退款中或退款完成状态", req.getId());
            return true;
        }
        if (req.getRefundFee().compareTo(userCaInfo.getPrice()) == 1) {
            log.error(":>>> 订单:{} 退款金额:{} 不能大于用户支付金额:{}", req.getId(), req.getRefundFee(), userCaInfo.getPrice());
            throw new BusinessException("退款金额不能大于支付金额");
        }
        Update update = new Update();
        update.set("verifyStatus", 1);
        update.set("caStatus", 9);
        update.set("refundFee", req.getRefundFee());
        update.set("refundDesc", req.getRefundDesc());
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, UserCaInfo.class);
        log.info(":>>> 订单:{} 的退款预操作结果:{}", req.getId(), updateResult.getModifiedCount() > 0 ? true : false);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean doRefund(UserCaInfo req) {
        log.info(":>>> 开始退款审核操作:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || Objects.isNull(req.getVerifyStatus()) || StringUtils.isBlank(req.getId()) || StringUtils.isBlank(req.getRefundDesc())) {
            log.error(":>>> 退款审核操作参数错误");
            throw new BusinessException("退款审核操作参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        UserCaInfo userCaInfo = mongoTemplate.findOne(query, UserCaInfo.class);
        if (Objects.isNull(userCaInfo)) {
            log.error(":>>> 证书订单不存在:{}", userCaInfo.getId());
            throw new BusinessException("证书订单不存在");
        }
        UserPO userPO = visitorUserService.getUser(userCaInfo.getUserId());
        if (Objects.isNull(userPO)) {
            log.error(":>>> 用户:{} 不存在", userCaInfo.getUserId());
            throw new BusinessException("证书用户不存在");
        }
        //状态,0:待付款,1:已付款,2:制作中,3:待发货,4:待收货,5:已收货,6:订单完成,7:订单关闭,8:售后中,9:退款中,10:待返货，11:返货中，12:退款完成
        if (!userCaInfo.getCaStatus().equals(9)) {
            log.error(":>>> 订单:{} 订单状态错误,不是退款中订单", req.getId());
            return false;
        }
        req.setRefundFee(userCaInfo.getRefundFee());
        if (req.getRefundFee().compareTo(userCaInfo.getPrice()) == 1) {
            log.error(":>>> 订单:{} 退款金额:{} 不能大于用户支付金额:{}", req.getId(), req.getRefundFee(), userCaInfo.getPrice());
            throw new BusinessException("退款金额不能大于支付金额");
        }
        QQWxRefundRequest request = new QQWxRefundRequest();

        switch (req.getPlatform()) {
            case "qq":
                request.setAppId(appId);
                break;
            case "wechat-h5":
                request.setApp(wxPayConfig.getWxAppId());
                break;
            default:
                break;
        }
        request.setOpenId(userPO.getOpenId());
        if (StringUtils.isNotBlank(userCaInfo.getParentOrderNo())) {
            request.setOrderNo(userCaInfo.getParentOrderNo());
        } else {
            request.setOrderNo(userCaInfo.getOrderNo());
        }
        request.setRefundFee(userCaInfo.getRefundFee());
        request.setDesc("国青实践纸质版证书退款");
        request.setTotalFee(userCaInfo.getPrice());
        String refundNo = super.generateOrderNo("ca-order.refund.no");
        request.setRefundNo(refundNo);
//        QQWxRefundResult refundResult = this.refund(request, req.getPlatform());
//        log.info(":>>> 订单:{},退款结果:{}", userCaInfo.getOrderNo(), JSON.toJSONString(refundResult));
//
//        if (Objects.isNull(refundResult) || StringUtils.isNotBlank(refundResult.getErrCode())) {
//            log.error(":>>> 订单:{},退款失败:{}", userCaInfo.getOrderNo(), refundResult.getErrCodeDes());
//            return false;
//        }
        Update update = new Update();
        update.set("verifyStatus", 2);
        update.set("caStatus", 12);
        update.set("refundId", refundNo);
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, UserCaInfo.class);
        log.info(":>>> 订单:{} 的退款操作结果:{}", req.getId(), updateResult.getModifiedCount() > 0 ? true : false);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean export(UserCaInfoQueryVO req, HttpServletResponse response) {
        log.info(":>>> 开始订单导出:{}", JSON.toJSONString(req));
        HashMap<String, UniversityInfo> universityInfoHashMap = new HashMap<>();
        Query query = new Query();
        this.setUserCaInfoQuery(query, req);
        List<UserCaInfo> contents = mongoTemplate.find(query, UserCaInfo.class);
        if (CollectionUtils.isEmpty(contents)) {
            log.error(":>>> 用户 订单为空");
            return false;
        }
        log.info(":>>> 订单总量:{}", contents.size());
        int counter = 0;
        HSSFWorkbook workbook = new HSSFWorkbook();
        HashMap<String, CaInfo> caInfoHashMap = new HashMap<>();

        try {
            OutputStream os = response.getOutputStream();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String dateStr = sdf.format(new Date());
            String fileName = "订单单-" + "-" + dateStr;
            this.setResponseHeader(response, fileName);
            int rowNum = 1;
            int sheetNum = 1;
            //创建一个Excel表单,参数为sheet的名字
            HSSFSheet sheet = workbook.createSheet("sheet" + "-" + sheetNum);
            setCaOrderExcelTitle(workbook, sheet);
            //创建表头
            for (UserCaInfo content : contents) {
                CaInfo caInfo = null;
                if (StringUtils.isNotBlank(content.getCaInfoId())) {
                    if (caInfoHashMap.containsKey(content.getCaInfoId())) {
                        caInfo = caInfoHashMap.get(content.getCaInfoId());
                    } else {
                        caInfo = this.findOne(content.getCaInfoId());
                        caInfoHashMap.put(content.getCaInfoId(), caInfo);
                    }

                }

                log.info(":>>> 导出行数:{}", counter);
                if (counter != 0 && counter % 60000 == 0) {
                    //超过60000就自动创建一个sheet
                    sheetNum++;
                    sheet = workbook.createSheet("sheet-" + sheetNum);
                    setCaOrderExcelTitle(workbook, sheet);
                    //创建表头
                    //新增数据行，并且设置单元格数据
                    rowNum = 1;
                    sheetNum++;
                    log.info(":>>> 操作60000行,创建新sheet");
                }
                HSSFRow row = sheet.createRow(rowNum);
                if (Objects.nonNull(caInfo)) {
                    if (StringUtils.isNotBlank(caInfo.getName())) {
                        row.createCell(0).setCellValue(caInfo.getName());
                    }
                    if (StringUtils.isNotBlank(caInfo.getGoodsCode())) {
                        row.createCell(1).setCellValue(content.getGoodsCode());
                    }
                }
                // 订单编号
                if (StringUtils.isNotBlank(content.getOrderNo())) {
                    row.createCell(2).setCellValue(content.getOrderNo());
                }
                // 订单状态
                String caStatusStr = this.getCaOrderStatusStr(content.getCaStatus());
                log.info(":>>> 订单状态:{}", caStatusStr);
                row.createCell(3).setCellValue(caStatusStr);
                if (StringUtils.isNotBlank(content.getLogisticsCompany())) {
                    row.createCell(4).setCellValue(content.getLogisticsCompany());
                }
                if (StringUtils.isNotBlank(content.getLogisticsNo())) {
                    row.createCell(5).setCellValue(content.getLogisticsNo());
                }
                if (StringUtils.isNotBlank(content.getBizName())) {
                    row.createCell(6).setCellValue(content.getBizName());
                }
                if (StringUtils.isNotBlank(content.getUniversityName())) {
                    row.createCell(7).setCellValue(content.getUniversityName());
                }
                if (StringUtils.isNotBlank(content.getName())) {
                    row.createCell(8).setCellValue(content.getName());
                }
                if (StringUtils.isNotBlank(content.getMobile())) {
                    row.createCell(9).setCellValue(content.getMobile());
                }
                if (Objects.nonNull(content.getPayTime())) {
                    String dateNowStr = TimeUtils.parse(content.getPayTime(), "yyyy-MM-dd HH:mm:ss");
                    row.createCell(10).setCellValue(dateNowStr);
                }
                if (Objects.nonNull(content.getMaterial())) {
                    switch (content.getMaterial()) {
                        case 0:
                            row.createCell(11).setCellValue("电子");
                            break;
                        case 1:
                            row.createCell(11).setCellValue("纸质");
                            break;
                        default:
                            break;
                    }
                }
                if (StringUtils.isNotBlank(content.getProvince())) {
                    row.createCell(12).setCellValue(content.getProvince());
                }
                if (StringUtils.isNotBlank(content.getCity())) {
                    row.createCell(13).setCellValue(content.getCity());
                }
                if (StringUtils.isNotBlank(content.getArea())) {
                    row.createCell(14).setCellValue(content.getArea());
                }
                if (StringUtils.isNotBlank(content.getAddress())) {
                    row.createCell(15).setCellValue(content.getAddress());
                }
                if (Objects.nonNull(content.getPrice())) {
                    row.createCell(16).setCellValue(content.getPrice() / 100);
                }
                if (Objects.nonNull(content.getTransactionId())) {
                    row.createCell(17).setCellValue(content.getTransactionId());
                }
                if (Objects.nonNull(content.getRefundFee())) {
                    row.createCell(18).setCellValue(content.getRefundFee());
                }
                if (Objects.nonNull(content.getReceiveTime())) {
                    String dateNowStr = TimeUtils.parse(content.getReceiveTime(), "yyyy-MM-dd HH:mm:ss");
                    row.createCell(19).setCellValue(dateNowStr);
                }
                if (StringUtils.isNotBlank(content.getTagsSearchStr())) {
                    row.createCell(20).setCellValue(content.getTagsSearchStr());
                }
                if (StringUtils.isNotBlank(content.getId())) {
                    row.createCell(21).setCellValue(content.getId());
                }
                if (StringUtils.isNotBlank(content.getPlatform())) {
                    row.createCell(22).setCellValue(content.getPlatform());
                }
                log.info(":>>> 大学ID:{}", content.getUniversityId());
                if (StringUtils.isNotBlank(content.getUniversityId())) {
                    if (universityInfoHashMap.containsKey(content.getUniversityId())) {
                        row.createCell(23).setCellValue(universityInfoHashMap.get(content.getUniversityId()).getName());
                    } else {
                        Query query1 = new Query().addCriteria(Criteria.where("id").is(content.getUniversityId()));
                        UniversityInfo universityInfo = mongoTemplate.findOne(query1, UniversityInfo.class);
                        if (Objects.nonNull(universityInfo)) {
                            row.createCell(23).setCellValue(universityInfo.getName());
                            universityInfoHashMap.put(content.getUniversityId(), universityInfo);
                        }
                    }
                }
                rowNum++;
                counter++;
            }
            //将excel写入到输出流中
            log.info(":>>> flush:{} 行", rowNum);
            workbook.write(os);
            os.flush();
            os.close();
            log.info(":>>> 订单内容导出,关闭文件流");
            return true;
        } catch (Exception e) {
            log.error(":>>> 订单内容导出错误:{}", e);
        }
        return false;
    }

    @Override
    public boolean createShippingBatch(ShippingBatch req) {
        log.info(":>>> 开始创建发货批次:{}", req);
        if (Objects.isNull(req) || StringUtils.isBlank(req.getActivityId())
                || StringUtils.isBlank(req.getBatchNo()) || StringUtils.isBlank(req.getName())
                || StringUtils.isBlank(req.getDesc()) || Objects.isNull(req.getNum())) {
            log.error(":>>> 创建发货批次参数错误");
            throw new BusinessException("创建发货批次参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getActivityId()));
        ActivityInfo exist = mongoTemplate.findOne(query, ActivityInfo.class);
        if (Objects.isNull(exist)) {
            log.error(":>>> 活动不存在:{}", req.getActivityId());
            throw new BusinessException("活动不存在");
        }
        req.setActivityName(exist.getName());
        req.setCreatedTime(new Date());
        mongoTemplate.insert(req);
        return true;
    }

    @Override
    public boolean updateShippingBatch(ShippingBatch req) {
        log.info(":>>> 开始修改发货批次:{}", req);
        if (Objects.isNull(req) || StringUtils.isBlank(req.getId())) {
            log.error(":>>> 修改发货批次参数错误");
            throw new BusinessException("修改发货批次参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(req.getId()));
        ShippingBatch shippingBatch = mongoTemplate.findOne(query, ShippingBatch.class);
        if (Objects.isNull(shippingBatch)) {
            log.error(":>>> 发货批次不存在:{}", req.getId());
            throw new BusinessException("发货批次");
        }
        // 活动ID不能修改
        Update update = new Update();
        update.set("updatedTime", new Date());
        if (StringUtils.isNotBlank(req.getName())) {
            update.set("name", req.getName());
        }
        if (StringUtils.isNotBlank(req.getDesc())) {
            update.set("desc", req.getDesc());
        }
        if (StringUtils.isNotBlank(req.getBatchNo())) {
            update.set("batchNo", req.getBatchNo());
        }
        if (Objects.nonNull(req.getNum())) {
            update.set("num", req.getNum());
        }
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, ShippingBatch.class);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    @Override
    public boolean deleteShippingBatch(String tenantId, String id) {
        log.info(":>>> 开始删除发货批次:{}", id);
        if (StringUtils.isBlank(id)) {
            log.error(":>>> 删除发货批次参数错误");
            throw new BusinessException("删除发货批次参数错误");
        }
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        mongoTemplate.remove(query, ShippingBatch.class);
        return true;
    }

    @Override
    public HashMap<String, Object> importShippingBatchDetail(MultipartFile file, String batchId, String tenantId) {
        if (StringUtils.isBlank(batchId)) {
            log.error(":>>> 批次:{} 为空", batchId);
            throw new BusinessException("批次号为空");
        }
        Query query1 = new Query().addCriteria(Criteria.where("id").is(batchId));
        ShippingBatch batch = mongoTemplate.findOne(query1, ShippingBatch.class);
        if (Objects.isNull(batch)) {
            log.error(":>>> 发货批次不存在:{}", batchId);
            throw new BusinessException("发货批次不存在");
        }
        HashMap<String, Object> result = new HashMap<>();
        Date date = new Date();
        log.info(":>>> 开始导入发货明细:{}");
        try {
            InputStream is = file.getInputStream();
            String fileName = file.getName();
//            if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
//                log.error(":>>> 批量导入Excel格式错误:{}", fileName);
//                throw new BusinessException("导入文件格式错误");
//            }
            boolean isExcel2003 = false;
            if (fileName.matches("^.+\\.(?i)(xlsx)$")) {
                isExcel2003 = false;
            }
            Date now = new Date();
            Workbook wb = null;
            if (isExcel2003) {
                wb = new HSSFWorkbook(is);
            } else {
                wb = new XSSFWorkbook(is);
            }
            Sheet sheet = wb.getSheetAt(0);
            List<HashMap<String, Object>> errors = new ArrayList<>();
            HashMap<String, UserCaInfo> datas = new HashMap<>();
            log.info(":>>> 表格的行数:{}", sheet.getLastRowNum());
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                HashMap<String, Object> errorMsg = new HashMap<>();
                log.info(":>>> 处理第:{} 行", i);
                Row row = sheet.getRow(i);
                if (Objects.isNull(row)) {
                    log.info(":>>> 处理第:{} 行为空跳出循环", i);
                    break;
                }
                Cell cell21 = row.getCell(21);
                Cell cell22 = row.getCell(22);
                Cell cell23 = row.getCell(23);
                if (Objects.isNull(cell21) || Objects.isNull(cell22) || Objects.isNull(cell23)) {
                    errorMsg.put("row", i + 1);
                    errorMsg.put("colum", 1);
                    errorMsg.put("message", "订单标示、物流公司、订单编号为空");
                    errors.add(errorMsg);
                    log.error(":>>> 第:{} 行格式错误,订单标示、物流公司、订单编号为空", i + 1);
                    continue;
                }
                String userCaInfoId = cell21.getStringCellValue();
                String wuliuCompany = cell22.getStringCellValue();
                String wuliuNo = cell23.getStringCellValue();
                try {
                    if (StringUtils.isBlank(userCaInfoId)) {
                        errorMsg.put("row", i + 1);
                        errorMsg.put("colum", 1);
                        errorMsg.put("message", "行格式错误,订单标示为空");
                        errors.add(errorMsg);
                        log.error(":>>> 第:{} 行格式错误,订单标示为空", i + 1);
                    }
                    if (StringUtils.isBlank(wuliuCompany)) {
                        errorMsg.put("row", i + 1);
                        errorMsg.put("colum", 1);
                        errorMsg.put("message", "物流公司为空");
                        errors.add(errorMsg);
                        log.error(":>>> 第:{} 行格式错误,物流公司为空", i + 1);
                    }
                    if (StringUtils.isBlank(wuliuNo)) {
                        errorMsg.put("row", i + 1);
                        errorMsg.put("colum", 1);
                        errorMsg.put("message", "物流编号为空");
                        errors.add(errorMsg);
                        log.error(":>>> 第:{} 行格式错误,物流编号为空", i + 1);
                    }
                    UserCaInfo userCaInfo = new UserCaInfo();
                    userCaInfo.setId(userCaInfoId);
                    userCaInfo.setLogisticsCompany(wuliuCompany);
                    userCaInfo.setLogisticsNo(wuliuNo);
                    datas.put(userCaInfoId, userCaInfo);
                } catch (Exception e) {
                    log.error(":>>> 第:{} 行格式错误", e);
                    errorMsg.put("row", i + 1);
                    errorMsg.put("colum", 1);
                    errorMsg.put("message", "手机号或销量格式错误,订单标示为空");
                    errors.add(errorMsg);
                    continue;
                }
            }
            if (!errors.isEmpty()) {
                log.error(":>>> 导入包含:{} 条错误信息", errors.size());
                result.put("error", errors);
                result.put("hasError", true);
                return result;
            }
            Integer unReg = 0;
            int i = 1;
            for (String m : datas.keySet()) {
                HashMap<String, Object> errorMsg = new HashMap<>();
                log.info(":>>> 开始处理第:{} 行", i);
                try {
                    Query query2 = new Query().addCriteria(Criteria.where("id").is(m));
                    UserCaInfo userCaInfo = mongoTemplate.findOne(query2, UserCaInfo.class);
                    if (Objects.isNull(userCaInfo)) {
                        log.info(":>>> 用户未更新:{}", m);
                        unReg++;
                        continue;
                    }
                } catch (Exception e) {
                    log.error(":>>> 处理数据错误:{}", e);
                    continue;
                }
                i++;
            }
            result.put("unReg", unReg);
            result.put("successCount", datas.keySet().size() - unReg);
            result.put("hasError", false);
            return result;
        } catch (Exception e) {
            log.error(":>>> 导入销量数据错误:{}", e);

        }
        return result;
    }

//    private QQWxRefundResult refund(QQWxRefundRequest request, String platform) {
//        log.info("refund发起退款 {}", JSON.toJSONString(request));
//        try {
//            log.info("平台:{},使用配置 APP ID：{}，商户号：{} 用户: [{}] 正在发起退款, 订单:[{}], 金额:{}"
//                    , platform, request.getAppId(), wxPayConfig.getMerchantId(), request.getOpenId(), request
//                    .getOrderNo(), request.getRefundFee());
//            String nonceStr = "" + new Date().getTime();
//            Map<String, Object> param = new HashMap<>();
//            String xmlRequest = buildRefundParamXml(request, nonceStr, param);
//            log.info(":>>> 生成的xml:{}", xmlRequest);
//            String nullKeys = param.keySet().stream().filter(k -> param.get(k) == null)
//                    .collect(Collectors.joining(","));
//            if (!nullKeys.isEmpty()) {
//                log.error(":>>> 请求参数为空");
//                throw new BusinessException("请求参数中有为空");
//            }
//            RefundRecord refundRecord = new RefundRecord();
//            BeanUtils.copyProperties(request, refundRecord);
//            refundRecord.setCreatedTime(new Date());
//            mongoTemplate.save(refundRecord);
//            log.info("refund : " + xmlRequest);
//            String refundUrl = wxPayConfig.getRefundQQWxUrl();
//            ResponseEntity<String> response = CommonUtils.getSSLRestTemplate(wxPayConfig.getCertPath(), wxPayConfig
//            .getMerchantId()).postForEntity(refundUrl, xmlRequest, String.class);
//            String body = response.getBody();
//            log.info(request.getOrderNo() + " result response:{}", body);
//            RefundOrderResult result = (RefundOrderResult) ParseXMLUtils.beginXMLParse(body, RefundOrderResult.class);
//            log.info(":>>> 退款请求结果:{}", JSON.toJSONString(result));
//            if (result == null) {
//                log.error(":>>> 发起退款失败");
//                throw new BusinessException("发起退款失败");
//            }
//            refundRecord.setResult(result);
//            mongoTemplate.save(refundRecord);
//            QQWxRefundResult refundResult = new QQWxRefundResult();
//            if (RefundOrderResult.SUCCESS.equals(result.getReturn_code())) {
//                //退款成功
//                if (RefundOrderResult.SUCCESS.equals(result.getResult_code())) {
//                    refundResult.setRefundId(result.getRefund_id());
//                    refundResult.setRefundFee(result.getRefund_fee());
//                } else {
//                    refundResult.setErrCode(result.getErr_code());
//                    refundResult.setErrCodeDes(result.getErr_code_des());
//                }
//            } else {
//                refundResult.setErrCode(result.getReturn_code());
//                refundResult.setErrCodeDes(result.getReturn_msg());
//            }
//            return refundResult;
//        } catch (Exception e) {
//            String msg = "退款异常";
//            log.error(msg + request.getOrderNo(), e);
//            throw new BusinessException("退款异常");
//        }
//    }

    private String buildRefundParamXml(QQWxRefundRequest request, String nonceStr, Map<String, Object> param) {
        StringBuffer bf = new StringBuffer();

        bf.append("<xml>");

        bf = appendElement(bf, "appid", wxPayConfig.getWxAppId(), param);

        bf = appendElement(bf, "mch_id", wxPayConfig.getMerchantId(), param);

        bf = appendElement(bf, "nonce_str", nonceStr, param);

        bf = appendElement(bf, "out_trade_no", request.getOrderNo(), param);

        bf = appendElement(bf, "out_refund_no", request.getRefundNo(), param);

        bf = appendElement(bf, "refund_fee", request.getRefundFee(), param);

        bf = appendElement(bf, "total_fee", request.getTotalFee(), param);

        bf = appendElement(bf, "refund_desc", request.getDesc(), param);

        String prestr = PayUtil.createLink(param);

        //MD5运算生成签名，这里是第一次签名，用于调用统一下单接口
        String sign = PayUtil.sign(prestr, wxPayConfig.getKey(), "utf-8").toUpperCase();
        log.info(":>>> 生成的签名:{}", sign);
        bf = appendElement(bf, "sign", sign, param);

        bf.append("</xml>");

        return bf.toString();
    }

    private StringBuffer appendElement(StringBuffer bf, String elementKey, Object elementValue,
                                       Map<String, Object> param) {
        bf.append("<" + elementKey + "><![CDATA[");
        bf.append(elementValue);
        bf.append("]]></" + elementKey + ">");
        if (param != null) {
            param.put(elementKey, elementValue);
        }
        return bf;
    }

    public void setResponseHeader(HttpServletResponse response, String fileName) {
        try {
            fileName = new String(fileName.getBytes());
            response.reset();
            response.setContentType("application/msexcel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Cache-Control", "no-cache");
        } catch (Exception ex) {
            log.error(":>>> setResponseHeader error:{}", ex);
        }
    }

    private void setCaOrderExcelTitle(HSSFWorkbook workbook, HSSFSheet sheet) {
        HSSFRow row = sheet.createRow(0);
        //设置列宽，setColumnWidth的第二个参数要乘以256，这个参数的单位是1/256个字符宽度
        sheet.setColumnWidth(0, 10 * 256);
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(2, 20 * 256);
        sheet.setColumnWidth(3, 100 * 256);

        //设置为居中加粗
        HSSFCellStyle style = workbook.createCellStyle();
        HSSFFont font = workbook.createFont();
        style.setFont(font);

        HSSFCell cell;
        cell = row.createCell(0);
        cell.setCellValue("证书名");
        cell.setCellStyle(style);

        cell = row.createCell(1);
        cell.setCellValue("证书编号");
        cell.setCellStyle(style);

        cell = row.createCell(2);
        cell.setCellValue("订单编号");
        cell.setCellStyle(style);

        cell = row.createCell(3);
        cell.setCellValue("订单状态");
        cell.setCellStyle(style);

        cell = row.createCell(4);
        cell.setCellValue("物流公司");
        cell.setCellStyle(style);

        cell = row.createCell(5);
        cell.setCellValue("物流编号");
        cell.setCellStyle(style);

        cell = row.createCell(6);
        cell.setCellValue("活动名称");
        cell.setCellStyle(style);

        cell = row.createCell(7);
        cell.setCellValue("大学");
        cell.setCellStyle(style);

        cell = row.createCell(8);
        cell.setCellValue("姓名");
        cell.setCellStyle(style);

        cell = row.createCell(9);
        cell.setCellValue("电话");
        cell.setCellStyle(style);

        cell = row.createCell(10);
        cell.setCellValue("支付时间");
        cell.setCellStyle(style);

        cell = row.createCell(11);
        cell.setCellValue("证书材质");
        cell.setCellStyle(style);
        cell = row.createCell(12);
        cell.setCellValue("省");
        cell.setCellStyle(style);
        cell = row.createCell(13);
        cell.setCellValue("市");
        cell.setCellStyle(style);
        cell = row.createCell(14);
        cell.setCellValue("区");
        cell.setCellStyle(style);
        cell = row.createCell(15);
        cell.setCellValue("地址");
        cell.setCellStyle(style);
        cell = row.createCell(16);
        cell.setCellValue("支付金额");
        cell.setCellStyle(style);
        cell = row.createCell(17);
        cell.setCellValue("微信交易号");
        cell.setCellStyle(style);
        cell = row.createCell(18);
        cell.setCellValue("退款金额");
        cell.setCellStyle(style);
        cell = row.createCell(19);
        cell.setCellValue("退款时间");
        cell.setCellStyle(style);
        cell = row.createCell(20);
        cell.setCellValue("订单备注");
        cell.setCellStyle(style);
        cell = row.createCell(21);
        cell.setCellValue("订单标示");
        cell.setCellStyle(style);
        cell = row.createCell(22);
        cell.setCellValue("平台");
        cell.setCellStyle(style);
        cell = row.createCell(23);
        cell.setCellValue("大学");
        cell.setCellStyle(style);

    }

    private void setUserCaInfoQuery(Query query, UserCaInfoQueryVO req) {
        if (StringUtils.isNotBlank(req.getName())) {
            query.addCriteria(Criteria.where("name").is(req.getName()));
        }
        if (StringUtils.isNotBlank(req.getMobile())) {
            Pattern pattern = Pattern.compile("^.*" + req.getOrderNo() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("mobile").regex(pattern));
        }
        if (Objects.nonNull(req.getMaterial())) {
            query.addCriteria(Criteria.where("material").is(req.getMaterial()));
        }
        if (StringUtils.isNotBlank(req.getUniversityId())) {
            query.addCriteria(Criteria.where("universityId").is(req.getUniversityId()));
        }
        if (StringUtils.isNotBlank(req.getActivityId())) {
            query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
        }
        if (StringUtils.isNotBlank(req.getOrderNo())) {

            Pattern pattern = Pattern.compile("^.*" + req.getOrderNo() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("orderNo").regex(pattern));
        }
        if (Objects.nonNull(req.getCaStatus())) {
            query.addCriteria(Criteria.where("csStatus").is(req.getCaStatus()));
        }
        if (Objects.nonNull(req.getVerifyStatus())) {
            query.addCriteria(Criteria.where("verifyStatus").is(req.getVerifyStatus()));
        }
        if (StringUtils.isNotBlank(req.getTag())) {
            Pattern pattern = Pattern.compile("^.*" + req.getTag() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("tagsSearchStr").regex(pattern));
        }
        if (StringUtils.isNotBlank(req.getTag())) {
            Pattern pattern = Pattern.compile("^.*" + req.getLogisticsNo() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("logisticsNo").regex(pattern));
        }
    }

    /**
     * 设置订单状态字符串
     *
     * @param caStatus
     * @return
     */
    private String getCaOrderStatusStr(Integer caStatus) {
        String status = " ";
        if (Objects.nonNull(caStatus)) {

            switch (caStatus) {
                case 0:
                    status = "待付款";
                    break;
                case 1:
                    status = "已付款";
                    break;
                case 2:
                    status = "制作中";
                    break;
                case 3:
                    status = "待发货";
                    break;
                case 4:
                    status = "待收货";
                    break;
                case 5:
                    status = "已收货";
                    break;
                case 6:
                    status = "订单完成";
                    break;
                case 7:
                    status = "订单关闭";
                    break;
                case 8:
                    status = "售后中";
                    break;
                case 9:
                    status = "退款中";
                    break;
                case 10:
                    status = "待返货";
                    break;
                case 11:
                    status = "返货中";
                    break;
                case 12:
                    status = "退款完成";
                    break;
                default:
                    break;
            }
        }
        return status;
    }

    /**
     * 检查子商品价格
     *
     * @param req
     */
    private void checkChildItemPrice(CaInfo req) {
        if (Boolean.FALSE.equals(req.getCombinedCommodity())) {
            return;
        }
        List<CaInfoMappping> mapppings = this.caInfoMapppingService.findByCaInfoId(req.getId());
        if (Objects.isNull(mapppings) || CollectionUtils.isEmpty(mapppings)) {
            return;
        }
        Integer totalPrice = 0;
        for (CaInfoMappping caInfoMappping : mapppings) {
            CaInfo c = this.findOne(caInfoMappping.getCaInfoId());
            if (Objects.nonNull(c) && Objects.nonNull(c.getPrice())) {
                totalPrice = c.getPrice();
            }
        }
//        if (totalPrice.compareTo(req.getPrice()) == 1) {
//            log.info(":>>> ");
//        }
    }
}
